#pragma semicolon 1

#include <sourcemod>
#include <sdktools>
#include <psycore>


#define PLUGIN_NAME "psyCore"
#define PLUGIN_VERSION "0.0.0.1"

#define EFFECTSIZE 66

enum rtdWaitMode
{
	WaitMode_All,
	WaitMode_Player
};


// Effect tracking
new g_iEffectId = 0;
new g_iEffectIdxMax = -1;
new Handle:g_hEffects = INVALID_HANDLE;
new Handle:g_hForwardDisableEffect;

// Globals for aux natives
new psyRTDGame:g_psyGame = psyRTDGame_Unknown;

public Plugin:myinfo = 
{
	name = PLUGIN_NAME,
	author = "psyduck,thrawn",
	description = "psySthSth Controller",
	version = PLUGIN_VERSION,
	url = "http://www.nicholashastings.com"
}


#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
	public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
#else
	public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max)
#endif
{
	RegPluginLibrary("psycore");
	
	CreateNative("psyRTD_RegisterEffect", Native_RegisterEffect);
	CreateNative("psyRTD_UnregisterEffect", Native_UnregisterEffect);
	CreateNative("psyRTD_GetGame", Native_GetGame);
	
	CreateNative("psyRTD_GetEffectName", Native_GetEffectName);
	CreateNative("psyRTD_GetEffectDescription", Native_GetEffectDescription);
	CreateNative("psyRTD_DoRandomEffect", Native_DoRandomEffect);
	CreateNative("psyRTD_DoEffect", Native_DoEffect);
	CreateNative("psyRTD_EffectIsNowDisabled", Native_EffectIsNowDisabled);
	
	// *FIXME* Maybe some renaming here to avoid confusion
	CreateNative("psyRTD_GetEffectCount", Native_GetEffectCount);
	CreateNative("psyRTD_GetEffectID", Native_GetEffectID);
	
	#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
		return APLRes_Success;
	#else
		return true;
	#endif
}

public OnPluginStart()
{
	DetectGame();
	
	g_hEffects = CreateArray(EFFECTSIZE);
	
	CreateConVar("psycore_version", PLUGIN_VERSION, "PsySthSth Controller", FCVAR_PLUGIN|FCVAR_NOTIFY);		
	
	g_hForwardDisableEffect = CreateGlobalForward("psyRTD_DisableEffect", ET_Ignore, Param_Cell, Param_Cell);
}

public Sort_Random_Func(elem1,elem2, const array[], Handle:hndl)
{
    return GetRandomInt(-1,1);
}


stock DoRandomEffect(client, type) {	
	if (!ClientCanHaveEffect(client))
	{
		LogMessage("client cant have effect");
		return -1;
	}
		
	new effectList[g_iEffectIdxMax+1];
	for(new i = 0; i <= g_iEffectIdxMax; i++) {
		effectList[i] = i;		
	}
	
	SortCustom1D(effectList, g_iEffectIdxMax+1, Sort_Random_Func);
	
	new Handle:effect;
	for (new i = 0; i < g_iEffectIdxMax+1; i++)	{
		effect = GetArrayCell(g_hEffects, effectList[i]);
		
		new etype = GetArrayCell(effect, 0);
		new eid = GetArrayCell(effect, 1);
		
		if(etype != type) {
			continue;
		}

		new psyRTDAction:result = psyRTD_Reroll;		
		Call_StartForward(GetArrayCell(effect, 4));
		Call_PushCell(client);
		Call_Finish(result);
		
		if (result == psyRTD_Continue)
		{
			return eid;
		} 
	}
	return -1;
}


public psyRTDAction:DoEffect(client, iEffectId) {
	new psyRTDAction:result = psyRTD_Reroll;

	if (g_iEffectIdxMax > -1)
	{
		for (new i = 0; i < g_iEffectIdxMax+1; i++)
		{
			new Handle:effect = GetArrayCell(g_hEffects, i);

			if (GetArrayCell(effect,1) == iEffectId)
			{			
				Call_StartForward(GetArrayCell(effect, 4));
				Call_PushCell(client);
				Call_Finish(result);

				break;				
			}	
		}
	}
	
	return result;
}

public bool:ClientCanHaveEffect(client)
{
	if (client < 1 || !IsClientInGame(client) || !IsPlayerAlive(client))
		return false;

	return true;
}

public Native_RegisterEffect(Handle:hPlugin, iNumParams)
{
	// RegisterPsyRTDEffect(psyRTDEffectType:effectType, const String:effectName[], const String:effectDesc[], psyRTDEffectCallback:func)
	new Handle:effect = CreateArray(15);
	
	PushArrayCell(effect, GetNativeCell(1));			// 0 
	new eId = g_iEffectId;
	g_iEffectId++;
	PushArrayCell(effect, eId);				// 1

	new String:tmpName[128];
	GetNativeString(2, tmpName, sizeof(tmpName)+1);
	PushArrayString(effect, tmpName);					// 2
	
	LogMessage("Registered Effect: %s (%i)", tmpName, eId);
	new String:tmpDesc[128];
	GetNativeString(3, tmpDesc, sizeof(tmpDesc)+1);
	PushArrayString(effect, tmpDesc);					// 3
		
	new Handle:fwd = CreateForward(ET_Single, Param_Cell, Param_CellByRef);	
	
	if (!AddToForward(fwd, hPlugin, GetNativeCell(4)))
	{
		decl String:szCallerName[PLATFORM_MAX_PATH];
		GetPluginFilename(hPlugin, szCallerName, sizeof(szCallerName));
		ThrowError("Failed to add forward from %s", szCallerName);
	}
	
	PushArrayCell(effect, fwd);							// 4
	PushArrayCell(g_hEffects, effect);
	g_iEffectIdxMax++;
	
	return eId;
}

public Native_UnregisterEffect(Handle:hPlugin, iNumParams)
{
	// *FIXME* Seems to be broken
	new iRTDEffectId = GetNativeCell(1);
	
	if (g_iEffectIdxMax > -1)
	{
		for (new i = 0; i < g_iEffectIdxMax+1; i++)
		{
			new Handle:effect = GetArrayCell(g_hEffects, i);
			
			if (GetArrayCell(effect,1) == iRTDEffectId)
			{
				CloseHandle(GetArrayCell(effect,4));
				RemoveFromArray(g_hEffects, i);
				g_iEffectIdxMax--;
				g_iEffectId--;
				i--;
			}
		}
	}
}

public Native_GetGame(Handle:hPlugin, iNumParams)
{
	return g_psyGame;
}

public Native_GetEffectCount(Handle:hPlugin, iNumParams)
{
	return GetArraySize(g_hEffects);
}

public Native_GetEffectID(Handle:hPlugin, iNumParams)
{
	new arrayID = GetNativeCell(1);
	
	// *FIXME* Add some checks here
	new Handle:effect;
	
	effect = GetArrayCell(g_hEffects, arrayID);		
	new eid = GetArrayCell(effect, 1);
	
	return eid;
}

//native psyRTD_DoRandomEffect(iClient, effectType);
public Native_DoRandomEffect(Handle:hPlugin, iNumParams)
{
	new iClient = GetNativeCell(1);
	new type = GetNativeCell(2);

	new efId = DoRandomEffect(iClient, type);
	//LogMessage("Client %N calls type %i (effectid: %i)", iClient, type, efId);

	return efId;
}

//native psyRTD_EffectIsNowDisabled(iClient);
public Native_EffectIsNowDisabled(Handle:hPlugin, iNumParams)
{
	new iClient = GetNativeCell(1);
	new effectId = GetNativeCell(2);
	
	Call_StartForward(g_hForwardDisableEffect);    
	Call_PushCell(iClient);
	Call_PushCell(effectId);
	Call_Finish();	
}

//native psyRTDAction:psyRTD_DoEffect(iClient, iEffectId);
public Native_DoEffect(Handle:hPlugin, iNumParams)
{
	new iClient = GetNativeCell(1);
	new iEffectId = GetNativeCell(2);
	
	return DoEffect(iClient, iEffectId);
}

//native psyRTD_GetEffectName(iRTDEffectId, effectType);
public Native_GetEffectName(Handle:hPlugin, iNumParams)
{
	new iRTDEffectId = GetNativeCell(1);
	
	//LogMessage("looking for effect: %i", iRTDEffectId);
	
	if (g_iEffectIdxMax > -1)
	{
		for (new i = 0; i < g_iEffectIdxMax+1; i++)
		{
			new Handle:effect = GetArrayCell(g_hEffects, i);

			if (GetArrayCell(effect,1) == iRTDEffectId)
			{			
				new String:effectTitle[128];
				GetArrayString(effect, 2, effectTitle, 128);
				//LogMessage("trying to pass %s", effectTitle);
				SetNativeString(2, effectTitle, 64, false);
			}	
		}
	}
}

//native psyRTD_GetEffectDescription(iRTDEffectId, effectType);
public Native_GetEffectDescription(Handle:hPlugin, iNumParams)
{
	new iRTDEffectId = GetNativeCell(1);
	
	if (g_iEffectIdxMax > -1)
	{
		for (new i = 0; i < g_iEffectIdxMax+1; i++)
		{
			new Handle:effect = GetArrayCell(g_hEffects, i);

			if (GetArrayCell(effect,1) == iRTDEffectId)
			{
				new String:effectDesc[64];
				GetArrayString(effect, 3, effectDesc, 64);
				
				SetNativeString(2, effectDesc, 64, false);				
			}			
			
			
		}
	}
}


DetectGame()
{
	// Adapted from HLX:CE ingame plugin :3
	if (g_psyGame == psyRTDGame_Unknown)
	{
		new String: szGameDesc[64];
		GetGameDescription(szGameDesc, 64, true);
		
		if (StrContains(szGameDesc, "Counter-Strike", false) != -1)
		{
			g_psyGame = psyRTDGame_CSS;
		}
		else if (StrContains(szGameDesc, "Day of Defeat", false) != -1)
		{
			g_psyGame = psyRTDGame_DODS;
		}
		else if (StrContains(szGameDesc, "Half-Life 2 Deathmatch", false) != -1)
		{
			g_psyGame = psyRTDGame_HL2MP;
		}
		else if (StrContains(szGameDesc, "Team Fortress", false) != -1)
		{
			g_psyGame = psyRTDGame_TF;
		}
		else if (StrContains(szGameDesc, "L4D", false) != -1 || StrContains(szGameDesc, "Left 4 D", false) != -1)
		{
			g_psyGame = (GuessSDKVersion() >= SOURCE_SDK_LEFT4DEAD) ? psyRTDGame_L4D : psyRTDGame_L4D2;
		}
		else if (StrContains(szGameDesc, "Insurgency", false) != -1)
		{
			g_psyGame = psyRTDGame_INSMOD;
		}
		else if (StrContains(szGameDesc, "Fortress Forever", false) != -1)
		{
			g_psyGame = psyRTDGame_FF;
		}
		else if (StrContains(szGameDesc, "ZPS", false) != -1)
		{
			g_psyGame = psyRTDGame_ZPS;
		}
		else if (StrContains(szGameDesc, "Age of Chivalry", false) != -1)
		{
			g_psyGame = psyRTDGame_AOC;
		}
		// game could not detected, try further
		if (g_psyGame == psyRTDGame_Unknown)
		{
			new String: szGameDir[64];
			GetGameFolderName(szGameDir, 64);
			
			if (StrContains(szGameDir, "cstrike", false) != -1)
			{
				g_psyGame = psyRTDGame_CSS;
			}
			else if (StrContains(szGameDir, "dod", false) != -1)
			{
				g_psyGame = psyRTDGame_DODS;
			}
			else if (StrContains(szGameDir, "hl2mp", false) != -1 || StrContains(szGameDir, "hl2ctf", false) != -1)
			{
				g_psyGame = psyRTDGame_HL2MP;
			}
			else if (StrContains(szGameDir, "fistful_of_frags", false) != -1)
			{
				g_psyGame = psyRTDGame_FOF;
			}
			else if (StrContains(szGameDir, "tf", false) != -1)
			{
				g_psyGame = psyRTDGame_TF;
			}
			else if (StrContains(szGameDir, "left4dead", false) != -1)
			{
				g_psyGame = (GuessSDKVersion() == SOURCE_SDK_LEFT4DEAD) ? psyRTDGame_L4D : psyRTDGame_L4D2;
			}
			else if (StrContains(szGameDir, "insurgency", false) != -1)
			{
				g_psyGame = psyRTDGame_INSMOD;
			}
			else if (StrContains(szGameDir, "FortressForever", false) != -1)
			{
				g_psyGame = psyRTDGame_FF;
			}
			else if (StrContains(szGameDir, "zps", false) != -1)
			{
				g_psyGame = psyRTDGame_ZPS;
			}
			else if (StrContains(szGameDir, "ageofchivalry", false) != -1)
			{
				g_psyGame = psyRTDGame_AOC;
			}
			else if (StrContains(szGameDir, "gesource", false) != -1)
			{
				g_psyGame = psyRTDGame_GES;
			}
		}
	}
}